package com.xp.flow.component;

import com.yomahub.liteflow.annotation.LiteflowComponent;
import lombok.extern.slf4j.Slf4j;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 脚本执行组件
 * 支持JavaScript代码执行，提供输入数据和上下文处理
 * 
 * @author AI Assistant
 * @since 1.0.0
 */
@Slf4j
@LiteflowComponent("SCRIPT_EXECUTE")
public class ScriptExecuteComponent extends BaseFlowComponent {

    private final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
    private final ExecutorService executorService = Executors.newCachedThreadPool();

    @Override
    public void process() {
        // 验证必需配置
        validateRequiredConfig("script");
        
        // 获取配置
        String script = getConfigValue("script", "");
        String language = getConfigValue("language", "javascript");
        Integer timeout = getConfigValue("timeout", 30);
        String description = getConfigValue("description", "脚本执行");
        
        logExecution("开始执行脚本: %s, 语言: %s, 超时: %d秒", description, language, timeout);
        
        // 获取输入数据
        Map<String, Object> inputData = getInputData();
        
        try {
            // 执行脚本
            Object result = executeScriptWithTimeout(script, language, inputData, timeout);
            
            // 处理执行结果
            Map<String, Object> outputData = processScriptResult(result, inputData);
            
            // 设置输出数据
            setOutputData(outputData);
            
            logExecution("脚本执行成功，结果类型: %s", result != null ? result.getClass().getSimpleName() : "null");
            
        } catch (TimeoutException e) {
            logError("脚本执行超时: " + timeout + "秒", e);
            handleScriptError("脚本执行超时", e, inputData);
            
        } catch (Exception e) {
            logError("脚本执行失败", e);
            handleScriptError("脚本执行失败: " + e.getMessage(), e, inputData);
        }
    }
    
    /**
     * 带超时的脚本执行
     * 
     * @param script 脚本代码
     * @param language 脚本语言
     * @param inputData 输入数据
     * @param timeoutSeconds 超时时间（秒）
     * @return 执行结果
     * @throws Exception 执行异常
     */
    private Object executeScriptWithTimeout(String script, String language, Map<String, Object> inputData, int timeoutSeconds) throws Exception {
        Future<Object> future = executorService.submit(() -> {
            try {
                return executeScript(script, language, inputData);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        
        try {
            return future.get(timeoutSeconds, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            throw e;
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            } else if (cause instanceof Exception) {
                throw (Exception) cause;
            } else {
                throw new RuntimeException(cause);
            }
        }
    }
    
    /**
     * 执行脚本
     * 
     * @param script 脚本代码
     * @param language 脚本语言
     * @param inputData 输入数据
     * @return 执行结果
     * @throws ScriptException 脚本执行异常
     */
    private Object executeScript(String script, String language, Map<String, Object> inputData) throws ScriptException {
        // 获取脚本引擎
        ScriptEngine engine = getScriptEngine(language);
        
        // 设置输入数据到脚本上下文
        setupScriptContext(engine, inputData);
        
        // 执行脚本
        return engine.eval(script);
    }
    
    /**
     * 获取脚本引擎
     * 
     * @param language 脚本语言
     * @return 脚本引擎
     */
    private ScriptEngine getScriptEngine(String language) {
        ScriptEngine engine;
        
        switch (language.toLowerCase()) {
            case "javascript":
            case "js":
                engine = scriptEngineManager.getEngineByName("javascript");
                if (engine == null) {
                    // 尝试使用Nashorn引擎
                    engine = scriptEngineManager.getEngineByName("nashorn");
                }
                break;
            default:
                engine = scriptEngineManager.getEngineByName(language);
        }
        
        if (engine == null) {
            throw new RuntimeException("不支持的脚本语言: " + language);
        }
        
        return engine;
    }
    
    /**
     * 设置脚本执行上下文
     * 
     * @param engine 脚本引擎
     * @param inputData 输入数据
     */
    private void setupScriptContext(ScriptEngine engine, Map<String, Object> inputData) {
        // 设置输入数据
        if (inputData != null) {
            for (Map.Entry<String, Object> entry : inputData.entrySet()) {
                engine.put(entry.getKey(), entry.getValue());
            }
            // 设置input变量为整个输入数据
            engine.put("input", inputData);
        }
        
        // 设置常用工具函数
        engine.put("log", new ScriptLogger());
        engine.put("JSON", new JsonHelper());
    }
    
    /**
     * 处理脚本执行结果
     * 
     * @param result 脚本执行结果
     * @param inputData 原始输入数据
     * @return 处理后的输出数据
     */
    private Map<String, Object> processScriptResult(Object result, Map<String, Object> inputData) {
        Map<String, Object> outputData = new HashMap<>(inputData);
        
        // 设置脚本执行结果
        outputData.put("scriptResult", result);
        outputData.put("success", true);
        
        // 如果结果是Map，合并到输出数据中
        if (result instanceof Map) {
            Map<String, Object> resultMap = (Map<String, Object>) result;
            outputData.putAll(resultMap);
        }
        
        return outputData;
    }
    
    /**
     * 处理脚本执行错误
     * 
     * @param errorMessage 错误消息
     * @param exception 异常
     * @param inputData 输入数据
     */
    private void handleScriptError(String errorMessage, Exception exception, Map<String, Object> inputData) {
        Map<String, Object> errorData = new HashMap<>(inputData);
        errorData.put("error", true);
        errorData.put("errorMessage", errorMessage);
        errorData.put("success", false);
        
        setOutputData(errorData);
    }
    
    /**
     * 脚本日志工具类
     */
    public static class ScriptLogger {
        public void info(String message) {
            log.info("[Script] {}", message);
        }
        
        public void warn(String message) {
            log.warn("[Script] {}", message);
        }
        
        public void error(String message) {
            log.error("[Script] {}", message);
        }
    }
    
    /**
     * JSON工具类
     */
    public static class JsonHelper {
        public String stringify(Object obj) {
            try {
                return new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(obj);
            } catch (Exception e) {
                return obj != null ? obj.toString() : "null";
            }
        }
        
        public Object parse(String json) {
            try {
                return new com.fasterxml.jackson.databind.ObjectMapper().readValue(json, Object.class);
            } catch (Exception e) {
                throw new RuntimeException("JSON解析失败: " + e.getMessage());
            }
        }
    }
}